package org.example.gxpt.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.example.gxpt.common.constant.*;
import org.example.gxpt.common.context.LoginContext;
import org.example.gxpt.common.context.SecurityContext;
import org.example.gxpt.common.properties.JwtProperties;
import org.example.gxpt.common.properties.RedisProperties;
import org.example.gxpt.common.result.PageResult;
import org.example.gxpt.common.result.Result;
import org.example.gxpt.common.utils.JwtUtil;
import org.example.gxpt.mapper.UserMapper;
import org.example.gxpt.mapper.UserRoleMapper;
import org.example.gxpt.pojo.dto.LoginDTO;
import org.example.gxpt.pojo.dto.UserDTO;
import org.example.gxpt.pojo.dto.UserPageQueryDTO;
import org.example.gxpt.pojo.dto.UserUpdateDTO;
import org.example.gxpt.pojo.entity.User;
import org.example.gxpt.pojo.entity.UserRole;
import org.example.gxpt.pojo.userdetails.LoginUser;
import org.example.gxpt.pojo.vo.UserLoginVO;
import org.example.gxpt.service.UserService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtProperties jwtProperties;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedisProperties redisProperties;

    @Autowired
    private UserRoleMapper userRoleMapper;

    /**
     * 保存新用户
     *
     * @param userDTO
     */
    @Override
    @Transactional
    public void save(UserDTO userDTO) {

        User user = new User();
        BeanUtils.copyProperties(userDTO, user);
        //user.setPassword(DigestUtils.md5DigestAsHex(userDTO.getPassword().getBytes()));
        user.setPassword(passwordEncoder.encode(userDTO.getPassword()));
        try {
            userMapper.insert(user);
            UserRole userRole = new UserRole();
            userRole.setUsername(user.getUsername());
            userRole.setPhone(user.getPhone());
            userRole.setRole(RoleConstant.USER);
            userRole.setRoleId(user.getId());
            userRoleMapper.insert(userRole);
        } catch (Exception e) {
            throw new RuntimeException("新增用户失败" + e);
        }
    }

    /**
     * 更新用户数据
     *
     * @param
     */
    @Override
    @Transactional
    public void updateById(UserUpdateDTO userUpdateDTO) {

        User user = new User();
        BeanUtils.copyProperties(userUpdateDTO, user);
        //user.setPassword(DigestUtils.md5DigestAsHex(userDTO.getPassword().getBytes()));
        user.setPassword(passwordEncoder.encode(userUpdateDTO.getPassword()));
        redisTemplate.opsForValue().getOperations().delete(RedisConstant.LOGIN_USER + user.getId());
        userMapper.update(user);
        UserRole userRole = UserRole.builder()
                .role(RoleConstant.USER)
                .roleId(user.getId())
                .phone(user.getPhone())
                .username(user.getUsername())
                .build();
        userRoleMapper.update(userRole);

        redisTemplate.opsForValue().getOperations().delete(RedisConstant.LOGIN_USER + user.getId());
    }

    /**
     * 分页查询
     *
     * @param userPageQueryDTO
     * @return
     */
    @Override
    public PageResult pageQuery(UserPageQueryDTO userPageQueryDTO) {
        PageHelper.startPage(userPageQueryDTO.getPage(), userPageQueryDTO.getPageSize());

        Page<User> page = userMapper.pageQuery(userPageQueryDTO);
        return new PageResult(page.getTotal(), page.getResult());

    }

    /**
     * 用户登陆
     *
     * @param loginDTO
     * @return
     */
    @Override
    public UserLoginVO login(LoginDTO loginDTO) {
        //存储当前登录用户的角色信息，在UserDetailsServiceImpl中取出用于判断当前登录用户的角色，从对应的mapper中查询
        LoginContext.setCurrentRole(LoginRoleConstant.USER);

        //AuthenticationManager authenticate进行认证
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(loginDTO.getUsername(), loginDTO.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);

        //如果认证没通过，给出对应提示
        if(Objects.isNull(authenticate)){
            throw new RuntimeException("登录失败");
        }

        //如果认证通过，使用生成一个jwt，存入loginVO中返回
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        User user = loginUser.getUser();

        //生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.ROLE, RoleConstant.USER);
        claims.put(JwtClaimsConstant.ID, user.getId());
        String token = JwtUtil.createJWT(
                jwtProperties.getSecretKey(),
                jwtProperties.getTtl(),
                claims);

        UserLoginVO userLoginVO = UserLoginVO.builder()
                .id(user.getId())
                .name(user.getName())
                .username(user.getUsername())
                .token(token)
                .avatar(user.getAvatar())
                .build();

        //把完整的用户信息存入redis key:userid value:User对象
        redisTemplate.opsForValue().set(RedisConstant.LOGIN_USER + user.getId().toString(), loginUser, redisProperties.getTtl(), TimeUnit.MILLISECONDS);
//        redisTemplate.opsForValue().set(RedisConstant.LOGIN_USER + token, loginUser, redisProperties.getTtl(), TimeUnit.MILLISECONDS);

        return userLoginVO;
    }

    /**
     * 根据id查询用户
     * @param id
     * @return
     */
    @Override
    public User getById(Long id) {
        return userMapper.getById(id);
    }

    /**
     * 用户登出
     *
     * @return
     */
    @Override
    public Result logout() {
        UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        Long id = loginUser.getUser().getId();
        //在Redis中删除用户信息
        redisTemplate.opsForValue().getOperations().delete(RedisConstant.LOGIN_USER + id);
        return Result.success();
    }

    /**
     * 根据上下文更新用户信息
     *
     * @param userUpdateDTO
     */
    @Override
    public void updateByContext(UserUpdateDTO userUpdateDTO) {
        User currentUser = SecurityContext.getCurrentLoginUser().getUser();
        if (Objects.isNull(currentUser)) {
            throw new RuntimeException("用户未登录");
        }
        Long userId = currentUser.getId();
        userUpdateDTO.setId(userId);
        User user = new User();
        BeanUtils.copyProperties(userUpdateDTO, user);
        userMapper.update(user);
        UserRole userRole = UserRole.builder()
                .role(RoleConstant.USER)
                .roleId(user.getId())
                .phone(user.getPhone())
                .username(user.getUsername())
                .build();
        userRoleMapper.update(userRole);
        redisTemplate.opsForValue().getOperations().delete(RedisConstant.LOGIN_USER + user.getId());

    }

    /**
     * 用户状态更新
     *
     * @param id
     * @param status
     */
    @Override
    public void updateStatus(Long id, Integer status) {
        userMapper.updateStatus(id, status);
        redisTemplate.opsForValue().getOperations().delete(RedisConstant.LOGIN_USER + id);
    }

    /**
     * 用户数据删除
     *
     * @param id
     */
    @Override
    @Transactional
    public void deleteUserById(Long id) {
        userMapper.deleteUserById(id);
        userRoleMapper.delete(UserRole.builder().roleId(id).role(RoleConstant.USER).build());
        redisTemplate.opsForValue().getOperations().delete(RedisConstant.LOGIN_USER + id);
    }

    /**
     * 查询所有用户
     *
     * @return
     */
    @Override
    public List<User> listAll() {
        return userMapper.listAll();
    }


}
